/**
 * 
 */
package il.ac.bgu.cs.heuristics.core;

import il.ac.bgu.cs.heuristics.graph.State;
import il.ac.bgu.cs.heuristics.graph.types.IActionGraph;
import il.ac.bgu.cs.heuristics.graph.types.IPath;
import il.ac.bgu.cs.heuristics.graph.types.IState;
import il.ac.bgu.cs.heuristics.graph.types.ISubstitution;
import il.ac.bgu.cs.heuristics.utils.CollectionUtils;
import il.ac.bgu.cs.heuristics.utils.StackSet;

import java.util.Set;

/**
 * @author haro
 * 
 */
public class HeuristicsEngineLight extends heuristicsEngine {

	private IState	init	= null;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * il.ac.bgu.cs.heuristics.core.IHeuristicsEngine#getHValue(il.ac.bgu.cs
	 * .heuristics.graph.types.IState,
	 * il.ac.bgu.cs.heuristics.graph.types.IState)
	 */

	public HeuristicsEngineLight(IActionGraph aGraph) {
		super(aGraph);
	}

	@Override
	public int getHValue(IState init, IState goal) {

		this.init = init;
		StackSet<IState> ss = CollectionUtils.newStackSet();
		aGraph.setInitState(init.clone());
		return extractLightHeuristic(goal, ss);
	}

	private int extractLightHeuristic(IState goal, StackSet<IState> ss) {

		log("+++++++ new call goal is: \n" + goal.toString());

		if (init.contains(goal)) {
			log("****************end call. hvalue is: " + 0);
			return 0;
		}
		if (!ss.push(goal)) {
			log("****************end call. hvalue is: " + -1);
			return -1;
		}

		Set<IState> notFilteredgoalsInAG = aGraph
				.getNonContradictingEffects(goal);
		Set<IState> goalsInAG = filterGoals(init, goal, notFilteredgoalsInAG,
				new NotUsefulGoalsStateFilter());

		if (goalsInAG.isEmpty())
			throw new IllegalArgumentException("the goalsInAG is empty");

		int minHvalue = Integer.MAX_VALUE;
		for (IState foundGoal : goalsInAG) {
			IPath path = aGraph.getShortestPath(init, foundGoal);
			IState subGoal = getNewGoalState(path.getConstraints());
			int currentGoalHvalue = extractLightHeuristic(subGoal, ss);
			if (currentGoalHvalue > -1) {
				currentGoalHvalue += path.weight();
				if (currentGoalHvalue < minHvalue)
					minHvalue = currentGoalHvalue;
			}
		}

		ss.pop();

		int retValue = minHvalue == Integer.MAX_VALUE ? -1 : minHvalue;
		log("****************end call. hvalue is: " + retValue);
		return retValue;
	}

	private IState getNewGoalState(Set<ISubstitution> subsSet) {
		Set<ISubstitution> subsInNewGoal = CollectionUtils.newLinkedHashSet();
		for (ISubstitution subs : subsSet) {
			if (!init.contains(subs)) {
				subsInNewGoal.add(subs);
			}
		}
		return State.createState(subsInNewGoal);

	}
}
